#include <cstdio>
#include <vector>
#include <string>

#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>
#include "sift/imgfeatures.h"
#include "sift/utils.h"

#include "inputimage.h"
#include "imagefeature.h"
#include "featurematcher.h"
#include "stitcher.h"
#include "imagematcher.h"
#include "imagematch.h"
#include "featureextractor.h"
#include "globalhomographycalculator.h"
#include "imageloader.h"
#include "commandlineoptions.h"
#include "enblendrenderer.h"
#include "opencvrenderer.h"
#include "imageset.h"
#include "componentfinder.h"

using namespace boost;
using namespace cshotel;
using namespace std;
using namespace sift;
using namespace cv;

int main(int argc, char** argv) {

	// seb was here - set kernel soft limits to be same as hard limits
	struct rlimit rlmt;
	getrlimit( RLIMIT_CPU, &rlmt );
	rlmt.rlim_cur = rlmt.rlim_max;
	setrlimit( RLIMIT_CPU, &rlmt );

    //read command line arguments
    CommandLineOptions::getInstance().initialize(argc, argv);
    if(!CommandLineOptions::getInstance().getContinueProgram()) {
        return 0;
    }
    
    shared_ptr<ImageSet> imageSet;
    
    // load images
    {
        ImageLoader imageLoader(CommandLineOptions::getInstance().getInputFileNames());
        imageSet = imageLoader.loadImages();
    }
    
    // extract features
    {
        FeatureExtractor featureExtractor(imageSet);
        featureExtractor.extract();
    }
    
    // match features
    {
        FeatureMatcher matcher(imageSet);
        matcher.match(4);
    }
    
    // match images
    {
        ImageMatcher imageMatcher(imageSet);
        imageMatcher.match(6);
    }
    
    if(CommandLineOptions::getInstance().getGenerateDebugImages()) {
        foreach(shared_ptr<InputImage> image, imageSet->getImages()) {
            InputImage::writeDebugImages(image);
            foreach(shared_ptr<ImageMatch> match, image->getImageMatches()) {
                match->writeDebugImages();
            }
        }
    }
    
    // find components
    shared_ptr<vector<shared_ptr<ImageSet> > > components;
    {
        ComponentFinder componentFinder(imageSet);
        components = componentFinder.findComponents();
    }
    dout("Using the largest component consisting of");
    imageSet = (*components)[0];
    foreach(shared_ptr<InputImage> img, imageSet->getImages()) {
        dout(" " << img->getId());
    }
    dout(endl);
    
    // calculate global homographies
    {
        GlobalHomographyCalculator homographyCalculator(imageSet);
        homographyCalculator.calculate();
    }
    
    #ifdef DEBUG
        foreach(shared_ptr<InputImage> image, imageSet->getImages()) {
            Mat h = image->getGlobalHomography();
            dout("Matrix of image " << image->getId() << " (det = " << determinant(h) << "):" << endl);
            dout("    " << h.at<double>(0, 0) << " " << h.at<double>(0, 1) << " " << h.at<double>(0, 2) << endl);
            dout("    " << h.at<double>(1, 0) << " " << h.at<double>(1, 1) << " " << h.at<double>(1, 2) << endl);
            dout("    " << h.at<double>(2, 0) << " " << h.at<double>(2, 1) << " " << h.at<double>(2, 2) << endl);
        }
    
    #endif
  
    // render
    if(CommandLineOptions::getInstance().getOutputRenderer() == CommandLineOptions::Enblend) {
        EnblendRenderer renderer(imageSet);
        renderer.render();
    } else if(CommandLineOptions::getInstance().getOutputRenderer() == CommandLineOptions::OpenCV) {
        OpenCVRenderer renderer(imageSet);
        renderer.render();
    } else if(CommandLineOptions::getInstance().getOutputRenderer() == CommandLineOptions::VisionWorkbench) {
    }
    
    return 0;
}
